import path, { posix, sep } from 'path'
import { commonInputFS } from './common'
import { normalizePath, _require } from './utils'

const suffixes = ['.mts', '.ts', '.tsx', '.mjs', '.cjs', '.js', '.jsx', '.json', '.node']
const ignoreSuffixRegExp = /\.(mts|tsx?|mjs|cjs|jsx?|json|node|bmp|jpe?g|png|gif|webp|svg)$/i
suffixes.push(...suffixes.map(suffix => sep + `index${suffix}`))

const safeRequire = (id: string) => {
  try {
    return _require(id)
  } catch {}
}

const safeRequireResolve = (id: string, options?: { paths?: string[] | undefined }) => {
  try {
    return _require.resolve(id, options)
  } catch {}
}

const stat = commonInputFS.stat

export const find = async (absPath: string) => {
  if (!ignoreSuffixRegExp.test(absPath) && !(await stat(absPath))?.isFile()) {
    for (const suffix of suffixes) {
      const resolvedId = absPath + suffix
      if ((await stat(resolvedId))?.isFile()) {
        return {
          id: resolvedId,
          external: false
        }
      }
    }
  }

  return {
    id: absPath,
    external: false
  }
}

export const nodeResolve = async (importee: string, importer: string | undefined, browser?: boolean, isEsm = true) => {
  if (/[\0\?\#,;]|^\.{2,}?:/.test(importee)) return

  let match
  if ((match = importee.match(/^((?:@[\w-]+\/)?[\w-]+)(\/.+)?$/))) {
    let [_, packageRelPath, fileName] = match
    const packageJsonPath = safeRequireResolve(
      posix.join(packageRelPath, 'package.json'),
      importer && path.isAbsolute(importer) ? { paths: [importer] } : undefined
    )
    if (!packageJsonPath) return

    const packageJson = safeRequire(packageJsonPath)
    if (!packageJson) return

    const packagePath = path.dirname(packageJsonPath)
    const exports = packageJson.exports
    if (exports && typeof exports === 'object') {
      fileName = fileName ? '.' : './' + normalizePath(posix.join('.', fileName))
      const result = exports[fileName]
      if (typeof result === 'string') {
        return { id: path.join(packagePath, result), external: false }
      } else if (result && typeof result === 'object') {
        if (browser && typeof result.browser === 'string') {
          return { id: path.join(packagePath, result.browser), external: false }
        }
        if (isEsm && typeof result.import === 'string') {
          return { id: path.join(packagePath, result.import), external: false }
        }
        if (typeof result.require === 'string') {
          return { id: path.join(packagePath, result.require), external: false }
        }
      }
    } else {
      if (isEsm && typeof packageJson.module === 'string') {
        return { id: path.join(packagePath, packageJson.module), external: false }
      }
      if (typeof packageJson.main === 'string') {
        return { id: path.join(packagePath, packageJson.main), external: false }
      }
      return find(path.join(packagePath, fileName))
    }
  } else {
    if (!path.isAbsolute(importee)) {
      if (!importer || !path.isAbsolute(importer)) return
      importee = path.join(path.dirname(importer), importee)
    } else {
      importee = path.normalize(importee)
    }

    return find(importee)
  }
}
